/**
 * Uploads/Downloads panel by Jared Brayshaw
 * 
 * Created for Task 148 on FogBugz:
 * https://msoe.fogbugz.com/default.asp?148
 * 
 * 
 */
package hydrap2p.gui;

import hydrap2p.download.DownloadPriority;
import hydrap2p.gui.commands.Cmd_DetailsPanel_RefreshDetails;
import hydrap2p.gui.commands.Cmd_FileTransferPanel_AddBTToDownloadQueue;
import hydrap2p.gui.commands.Cmd_FileTransferPanel_AddURLToDownloadQueue;
import hydrap2p.gui.commands.Cmd_FileTransferPanel_ChangeTransferPriority;
import hydrap2p.gui.commands.Cmd_FileTransferPanel_EditFilterLabels;
import hydrap2p.gui.commands.Cmd_FileTransferPanel_PauseTransfer;
import hydrap2p.gui.commands.Cmd_FileTransferPanel_RemoveFromDownloads;
import hydrap2p.gui.commands.Cmd_FileTransferPanel_ResumeTransfer;
import hydrap2p.gui.commands.CommandInvoker;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableColumn;

import org.apache.thrift.TException;

/**
 * A panel that displays all of the downloads currently enqueued.
 * 
 * @author Jared
 */
public class FileTransferPanel extends JScrollPane {
	//the table and its model used to display the downloads
	private final FileTransferTableModel model = new FileTransferTableModel();
	private final JTable table;

	private Map<Integer, Integer> indexMappings = new HashMap<Integer, Integer>();	//if not all the downloads are being displayed, their indexes won't match the table indexes, so we need to mitigate that
	private ArrayList<String> activeLabels = new ArrayList<String>();				//all displayed downloads must have the labels in this arraylist
	private boolean showUnlabeled = false;											//should downloads without a label also be displayed?
	private boolean refreshingTable = false;										//keeps track of whether or not the table is updating, so it doesn't trying to update itself in the middle of an update ("yo dawg...")

	//the popup menu and its listener
	private ActionListener contextListener = new MenuItemListener();
	private TablePopupMenu contextMenu = new TablePopupMenu();

	private static DownloadPriority selectedPriority = DownloadPriority.NORMAL;
	
	//singleton stuff
	private static volatile FileTransferPanel instance;

	public Map<Integer, Integer> getIndexMappings() {
		return indexMappings;
	}

	public static synchronized FileTransferPanel getInstance() {
		if (instance == null) {
			synchronized (FileTransferPanel.class) {
				if (instance == null) {
					instance = new FileTransferPanel();
				}
			}
		}
		return instance;
	}
	//end of singleton stuff
	
	
	/**
	 * Constructor that initializes the table
	 */
	private FileTransferPanel() {
		table = new JTable(model)
		{
			public int[] getSelectedRows()
			{
				int[] indexArrays = super.getSelectedRows();
				
				if (refreshingTable) return indexArrays;
				
				//TODO update indices to be the model indices, not the view ones
				for (int i = 0; i < indexArrays.length; i++)
				{
					int tableRow = FileTransferPanel.getInstance().getTable().convertRowIndexToModel(indexArrays[i]);
					int realRow = FileTransferPanel.getInstance().getIndexMappings().get(tableRow);
					indexArrays[i] = realRow;
				}
				
				return indexArrays;
			}
		};

		init();
		refreshDownloadList();
	}
	
	/**
	 * Creates the panel and sets all the table defaults
	 */
	private void init() {
		//allow sorting
		table.setAutoCreateRowSorter(true);
		
		contextMenu.refresh();
		
		//add the popup menu to the table
		table.setComponentPopupMenu(contextMenu);
		//table.addMouseListener(new PopupListener());
		
		//add it to the blank space too
		this.setComponentPopupMenu(contextMenu);
		//this.addMouseListener(new PopupListener());
		
		//keeps track of what downloads are currently selected
		table.getSelectionModel().addListSelectionListener(new RowListener());

		// set some defaults for the table
		TableColumn column;

		column = table.getColumnModel().getColumn(0);
		column.setCellRenderer(new TextRenderer());
		column.setMinWidth(50);

		column = table.getColumnModel().getColumn(1);
		column.setCellRenderer(new TextRenderer());
		column.setMinWidth(50);
		column.setMaxWidth(50);
		column.setResizable(false);

		column = table.getColumnModel().getColumn(2);
		column.setCellRenderer(new TextRenderer());
		column.setMinWidth(40);

		column = table.getColumnModel().getColumn(3);
		column.setCellRenderer(new BytesRenderer());
		column.setMinWidth(30);

		column = table.getColumnModel().getColumn(4);
		column.setCellRenderer(new ProgressRenderer());
		column.setMinWidth(60);

		column = table.getColumnModel().getColumn(5);
		column.setCellRenderer(new SpeedRenderer());
		column.setMinWidth(80);

		column = table.getColumnModel().getColumn(6);
		column.setCellRenderer(new SpeedRenderer());
		column.setMinWidth(70);
		
		//make the table the center of attention
		setViewportView(table);
	}
	
	/**
	 * Getter for the downloads table
	 * @return the downloads table
	 */
	public JTable getTable() {
		return table;
	}
	
	/**
	 * Called externally to tell this panel that the label filters have changed; refreshes the download
	 * list to reflect the new information.
	 * 
	 * @param labels			a list of the currently active labels
	 * @param showUnlabelled	whether or not unlabeled downloads should also show up in the table
	 */
	public void updateLabels(ArrayList<String> labels, boolean showUnlabeled) {
		activeLabels = labels;
		this.showUnlabeled = showUnlabeled;

		refreshDownloadList();
		
		//
		CommandInvoker.getInstance().invokeCommand(new Cmd_DetailsPanel_RefreshDetails());
	}
	
	/**
	 * Removes a label from any download listing that has it 
	 * 
	 * @param label		the label to remove
	 */
	public void labelDeleted(String label) {
		for (GUIDownloadListing gdl : GUIDataHandler.getInstance().getDownloadListing())
		{
			if (gdl.hasLabel(label))
				gdl.removeLabel(label);
		}
	}
	
	public void clearTableSelections(){
		refreshingTable = true;
		table.getSelectionModel().clearSelection();
		refreshingTable = false;
	}
	
	/**
	 * Updates the table of downloads to reflect the most recent data and filters
	 */
	public void refreshDownloadList() {
		refreshingTable = true;
		
		
		int tableSize = table.getRowCount();
		int[] selectedRows = table.getSelectedRows();
		List<GUIDownloadListing> selectedGDLs = new ArrayList<GUIDownloadListing>();
		
		for (int row : selectedRows)
		{
			if(row < GUIDataHandler.getInstance().getDownloadListing().size())
			{
				GUIDownloadListing gdl = GUIDataHandler.getInstance().getDownloadListing().get(row);
				selectedGDLs.add(gdl);
				
				try {
					GUIDataHandler.getInstance().log(GUIDataHandler.LOG_DEBUG, "FileTransferPanel", "Row saved: " + row + " for download " + gdl);
				} catch (TException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		
		int tableIndex = -1;
		int dataIndex = -1;
		
		double downSpeed = 0;
		double upSpeed = 0;
		
		// clear the data
		indexMappings.clear();
		while (model.getRowCount() > 0) {
			model.removeRow(0);
		}

		for (GUIDownloadListing gdl : GUIDataHandler.getInstance().getDownloadListing()) {
			
			downSpeed += gdl.getDownloadSpeed();
			upSpeed += gdl.getUploadSpeed();
			
			dataIndex++;
			if (gdl.hasNoLabels()) {
				if (showUnlabeled) {
					model.addListing(gdl);
					tableIndex++;
					indexMappings.put(tableIndex, dataIndex);
				}

				continue;
			} else {
				for (String curLabel : activeLabels) {
					if (gdl.hasLabel(curLabel)) {
						model.addListing(gdl);
						tableIndex++;
						indexMappings.put(tableIndex, dataIndex);
						
						break; // we only need one label to display the listing
					}
				}
			}
		}
		
		int toCountTo = table.getRowCount();
		if (toCountTo != tableSize) toCountTo = -1;
		
		//reselect all the listings that were selected before		
		//for every row, if it has a download listing we've selected before, reselect that one
		//if the labels are different, this loop won't run (toCountTo == -1)
		for (int i = 0; i < toCountTo; i++)
		{
			System.out.println(selectedGDLs);
			
			GUIDownloadListing gdl = GUIDataHandler.getInstance().getDownloadListing().get(i);
			
			if (selectedGDLs.contains(gdl))
			{
				try {
					GUIDataHandler.getInstance().log(GUIDataHandler.LOG_DEBUG, "FileTransferPanel", "Row loaded: " + gdl);
				} catch (TException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
				table.addRowSelectionInterval(i, i);
			}
		}
		
		
		refreshingTable = false;
		
		((GraphPanel)LoggingPanel.getInstance().graphArea).addSpeedPair(upSpeed, downSpeed);
	}
	
	/**
	 * An inner class to provide functionality to the popup menus
	 * 
	 * TODO: change to .equals()
	 * 
	 * @author Jared
	 */
	class MenuItemListener implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			if (e.getActionCommand() == "pause") {
				CommandInvoker.getInstance().invokeCommand(
						new Cmd_FileTransferPanel_PauseTransfer());
				System.out.println("Pause");
			} else if (e.getActionCommand() == "addURL") {
				CommandInvoker.getInstance().invokeCommand(
						new Cmd_FileTransferPanel_AddURLToDownloadQueue());
				System.out.println("Add URL");
			} else if (e.getActionCommand() == "addTorrent") {
				CommandInvoker.getInstance().invokeCommand(
						new Cmd_FileTransferPanel_AddBTToDownloadQueue());
				System.out.println("Add Torrent");
			}  else if (e.getActionCommand() == "resume") {
				CommandInvoker.getInstance().invokeCommand(
						new Cmd_FileTransferPanel_ResumeTransfer());
				System.out.println("Resume");
			} else if (e.getActionCommand() == "editFilterLabels") {
				CommandInvoker.getInstance().invokeCommand(
						new Cmd_FileTransferPanel_EditFilterLabels());
				System.out.println("Edit Filter Labels");
			} else if (e.getActionCommand() == "highPriority") {
				selectedPriority = DownloadPriority.HIGH;
				CommandInvoker.getInstance().invokeCommand(new Cmd_FileTransferPanel_ChangeTransferPriority());
				System.out.println("Change Priority: High");
			} else if (e.getActionCommand() == "normalPriority") {
				 selectedPriority = DownloadPriority.NORMAL;
				CommandInvoker.getInstance().invokeCommand(new Cmd_FileTransferPanel_ChangeTransferPriority());
				System.out.println("Change Priority: Normal");
			} else if (e.getActionCommand() == "lowPriority") {
				 selectedPriority = DownloadPriority.LOW;
				CommandInvoker.getInstance().invokeCommand(new Cmd_FileTransferPanel_ChangeTransferPriority());
				System.out.println("Change Priority: Low");
			} else if (e.getActionCommand() == "remove") {
				CommandInvoker.getInstance().invokeCommand(
						new Cmd_FileTransferPanel_RemoveFromDownloads());
				System.out.println("Remove From Downloads");
			} else if (e.getActionCommand() == "addToUploads") {
				// CommandInvoker.getInstance().invokeCommand(new
				// Cmd_FileTransferPanel_EditFilterLabels()());
				System.out.println("Add To Uploads");
			} else if (e.getActionCommand() == "removeFromUploads") {
				// CommandInvoker.getInstance().invokeCommand(new
				// Cmd_FileTransferPanel_EditFilterLabels()());
				System.out.println("Remove From Uploads");
			}
		}
	}
	
	public static DownloadPriority getSelectedPriority(){
		return selectedPriority;
	}
	
	/**
	 * An inner class representing the popup menu for this panel
	 * 
	 * @author Jared
	 */
	private class TablePopupMenu extends JPopupMenu {
		public TablePopupMenu() {
			super();
		}
		
		public void refresh()
		{
			int l = table.getSelectedRowCount();
			boolean rowHighlighted = (l > 0);
			rowHighlighted = true;
			
			addNewStringItem("Pause", "pause", rowHighlighted);
			addNewStringItem("Resume", "resume", rowHighlighted);
			addNewStringItem("Add URL...", "addURL", true);
			addNewStringItem("Add Torrent...", "addTorrent", true);
			addNewStringItem("Edit Filter Labels", "editFilterLabels", true);
			JMenu priority = new JMenu("Change Priority");
			priority.addActionListener(contextListener);
			priority.setEnabled(rowHighlighted);
				addNewSubItem(priority, "High", "highPriority", rowHighlighted);
				addNewSubItem(priority, "Normal", "normalPriority", rowHighlighted);
				addNewSubItem(priority, "Low", "lowPriority", rowHighlighted);
				this.add(priority);
			addNewStringItem("Remove", "remove", rowHighlighted);
			// addNewStringItem("Remove from Downloads", "removeFromDownloads");
			// addNewStringItem("Add to Uploads", "addToUploads");
			// addNewStringItem("Remove from Uploads", "removeFromUploads");

		}

		@Override
		public void show(Component c, int x, int y) {
			int[] l = table.getSelectedRows();
			super.show(c, x, y);
		}
		
		/**
		 * Adds a new item to the menu and gives it an actionlistener 
		 * 
		 * @param name		the text to display
		 * @param command	the command for the event fired
		 */
		private void addNewStringItem(String name, String command, boolean enabled) {
			//System.out.println("for item " + name + ", enabled flag is set to " + enabled);
			
			JMenuItem temp = new JMenuItem(name);
			temp.addActionListener(contextListener);
			temp.setActionCommand(command);
			temp.setEnabled(enabled);
			this.add(temp);
		}
		
		private void addNewSubItem(JMenu parent, String name, String command, boolean enabled) {
			//System.out.println("for item " + name + ", enabled flag is set to " + enabled);
			JMenuItem temp = new JMenuItem(name);
			temp.addActionListener(contextListener);
			temp.setActionCommand(command);
			temp.setEnabled(enabled);
			parent.add(temp);
		}
	}
	
	/**
	 * An inner class shows the popup menu when appropriate
	 * 
	 * @author Jared
	 *
	class PopupListener extends MouseAdapter {
		public void mousePressed(MouseEvent e) {
			contextMenu = new TablePopupMenu();
			contextMenu.refresh();
			maybeShowPopup(e);
		}

		public void mouseReleased(MouseEvent e) {
			maybeShowPopup(e);
		}

		private void maybeShowPopup(MouseEvent e) {
			if (e.isPopupTrigger()) {
				contextMenu.show(e.getComponent(), e.getX(), e.getY());
			}
		}
	}
	
	/**
	 * An inner class that refreshes the details panel when the user selects a download listing
	 * 
	 * @author Jared
	 */
	private class RowListener implements ListSelectionListener {
		public void valueChanged(ListSelectionEvent event) {
			if (event.getValueIsAdjusting()) {
				return;
			}
			
			if (!refreshingTable) {
				/*contextMenu = new TablePopupMenu();
				contextMenu.refresh();
				table.setComponentPopupMenu(contextMenu);*/
				
				CommandInvoker.getInstance().invokeCommand(new Cmd_DetailsPanel_RefreshDetails());
			}
		}
	}
}
